]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
[StWidget] add (optional) hover tracking
authorDan Winship <danw@gnome.org>
Fri, 19 Mar 2010 17:47:34 +0000 (13:47 -0400)
committerDan Winship <danw@gnome.org>
Wed, 24 Mar 2010 14:03:50 +0000 (10:03 -0400)
If track-hover is set, update the hover property automatically, and
the "hover" pseudo class to match, as StClickable used to do. (Remove
the corresponding code in StClickable). Tweak the tooltip handling to
use track-hover, which also makes it slightly more reliable in the
presence of reactive children, etc.

js/ui/appDisplay.js
js/ui/workspacesView.js
src/st/st-clickable.c
src/st/st-widget.c
src/st/st-widget.h

index 9ec427f1af239198956c5b8053efc5e26ab55228..a8f878efa913849cceb7a8d63f4fa04eab305a79 100644 (file)
@@ -536,6 +536,8 @@ AppWellIcon.prototype = {
     },
 
     _onMenuPoppedDown: function() {
+        this.actor.sync_hover();
+
         if (this._didActivateWindow)
             return;
         if (!this._setWindowSelection)
index 5712509e3ad3cfad726d80b582b564fbfa1aca49..f8d92dfd3f3bbe889c4891845b9c4c42a976d1f8 100644 (file)
@@ -484,10 +484,7 @@ NewWorkspaceArea.prototype = {
     },
 
     setStyle: function(isHover) {
-        if (isHover)
-            this._child1.add_style_pseudo_class('hover');
-        else
-            this._child1.remove_style_pseudo_class('hover');
+        this._child1.set_hover(isHover);
     }
 };
 
index ee52e3738b46e55e1cb8da1b9675143ded00207c..2ff8758174bdfef86101953732582ea5326d02d0 100644 (file)
@@ -16,7 +16,6 @@ G_DEFINE_TYPE (StClickable, st_clickable, ST_TYPE_BIN);
 struct _StClickablePrivate {
   gboolean active;
   gboolean held;
-  gboolean hover;
   gboolean pressed;
 
   guint initiating_button;
@@ -33,7 +32,6 @@ enum {
   PROP_0,
 
   PROP_ACTIVE,
-  PROP_HOVER,
   PROP_PRESSED,
 };
 
@@ -46,11 +44,6 @@ sync_pseudo_class (StClickable *self)
     st_widget_add_style_pseudo_class (ST_WIDGET (self), "pressed");
   else
     st_widget_remove_style_pseudo_class (ST_WIDGET (self), "pressed");
-
-  if (self->priv->hover)
-    st_widget_add_style_pseudo_class (ST_WIDGET (self), "hover");
-  else
-    st_widget_remove_style_pseudo_class (ST_WIDGET (self), "hover");
 }
 
 static void
@@ -64,17 +57,6 @@ set_active (StClickable  *self,
   g_object_notify (G_OBJECT (self), "active");
 }
 
-static void
-set_hover (StClickable  *self,
-           gboolean      hover)
-{
-  if (self->priv->hover == hover)
-    return;
-  self->priv->hover = hover;
-  sync_pseudo_class (self);
-  g_object_notify (G_OBJECT (self), "hover");
-}
-
 static void
 set_pressed (StClickable  *self,
              gboolean      pressed)
@@ -102,21 +84,18 @@ st_clickable_enter_event (ClutterActor         *actor,
                           ClutterCrossingEvent *event)
 {
   StClickable *self = ST_CLICKABLE (actor);
-
-  if (st_clickable_contains (self, event->related))
-    return TRUE;
-  if (!st_clickable_contains (self, event->source))
-    return TRUE;
+  gboolean result;
 
   g_object_freeze_notify (G_OBJECT (actor));
 
-  if (self->priv->held)
-    set_pressed (self, TRUE);
-  set_hover (self, TRUE);
+  result = CLUTTER_ACTOR_CLASS (st_clickable_parent_class)->enter_event (actor, event);
+
+  /* We can't just assume get_hover() is TRUE; see st_widget_enter(). */
+  set_pressed (self, self->priv->held && st_widget_get_hover (ST_WIDGET (actor)));
 
   g_object_thaw_notify (G_OBJECT (actor));
 
-  return TRUE;
+  return result;
 }
 
 static gboolean
@@ -124,14 +103,18 @@ st_clickable_leave_event (ClutterActor         *actor,
                               ClutterCrossingEvent *event)
 {
   StClickable *self = ST_CLICKABLE (actor);
+  gboolean result;
 
-  if (st_clickable_contains (self, event->related))
-    return TRUE;
+  g_object_freeze_notify (G_OBJECT (actor));
 
-  set_hover (self, FALSE);
-  set_pressed (self, FALSE);
+  result = CLUTTER_ACTOR_CLASS (st_clickable_parent_class)->leave_event (actor, event);
 
-  return TRUE;
+  /* As above, we can't just assume get_hover() is FALSE. */
+  set_pressed (self, self->priv->held && st_widget_get_hover (ST_WIDGET (actor)));
+
+  g_object_thaw_notify (G_OBJECT (actor));
+
+  return result;
 }
 
 static gboolean
@@ -243,9 +226,6 @@ st_clickable_get_property (GObject         *object,
     case PROP_PRESSED:
       g_value_set_boolean (value, self->priv->pressed);
       break;
-    case PROP_HOVER:
-      g_value_set_boolean (value, self->priv->hover);
-      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -297,20 +277,6 @@ st_clickable_class_init (StClickableClass *klass)
                                                          FALSE,
                                                          G_PARAM_READWRITE));
 
-  /**
-   * StClickable:hover
-   *
-   * This property tracks whether the mouse is over the button; note this
-   * state is independent of whether the button is pressed.
-   */
-  g_object_class_install_property (gobject_class,
-                                   PROP_HOVER,
-                                   g_param_spec_boolean ("hover",
-                                                         "Hovering state",
-                                                         "Whether the mouse is over the button",
-                                                         FALSE,
-                                                         G_PARAM_READABLE));
-
   /**
    * StClickable:pressed
    *
@@ -333,4 +299,5 @@ st_clickable_init (StClickable *self)
 {
   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ST_TYPE_CLICKABLE,
                                             StClickablePrivate);
+  st_widget_set_track_hover (ST_WIDGET (self), TRUE);
 }
index 8a3b0340615aa3016ecf4c7f4639e210860c352d..01bee64fb867362fdbaf585165c9da97e2163ebf 100644 (file)
@@ -76,6 +76,8 @@ struct _StWidgetPrivate
   gboolean      is_style_dirty : 1;
   gboolean      draw_bg_color : 1;
   gboolean      draw_border_internal : 1;
+  gboolean      track_hover : 1;
+  gboolean      hover : 1;
 
   StTooltip    *tooltip;
 
@@ -101,11 +103,11 @@ enum
   PROP_PSEUDO_CLASS,
   PROP_STYLE_CLASS,
   PROP_STYLE,
-
   PROP_STYLABLE,
-
   PROP_HAS_TOOLTIP,
-  PROP_TOOLTIP_TEXT
+  PROP_TOOLTIP_TEXT,
+  PROP_TRACK_HOVER,
+  PROP_HOVER
 };
 
 enum
@@ -167,6 +169,14 @@ st_widget_set_property (GObject      *gobject,
       st_widget_set_tooltip_text (actor, g_value_get_string (value));
       break;
 
+    case PROP_TRACK_HOVER:
+      st_widget_set_track_hover (actor, g_value_get_boolean (value));
+      break;
+
+    case PROP_HOVER:
+      st_widget_set_hover (actor, g_value_get_boolean (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
       break;
@@ -212,6 +222,14 @@ st_widget_get_property (GObject    *gobject,
       g_value_set_string (value, st_widget_get_tooltip_text (actor));
       break;
 
+    case PROP_TRACK_HOVER:
+      g_value_set_boolean (value, priv->track_hover);
+      break;
+
+    case PROP_HOVER:
+      g_value_set_boolean (value, priv->hover);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
       break;
@@ -1125,15 +1143,34 @@ st_widget_get_theme_node (StWidget *widget)
   return priv->theme_node;
 }
 
+static gboolean
+actor_contains (ClutterActor *widget,
+                ClutterActor *other)
+{
+  while (other != NULL && other != widget)
+    other = clutter_actor_get_parent (other);
+  return other != NULL;
+}
+
 static gboolean
 st_widget_enter (ClutterActor         *actor,
                  ClutterCrossingEvent *event)
 {
   StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 
-
-  if (priv->has_tooltip)
-    st_widget_show_tooltip ((StWidget*) actor);
+  if (priv->track_hover)
+    {
+      if (actor_contains (actor, event->source))
+        st_widget_set_hover (ST_WIDGET (actor), TRUE);
+      else
+        {
+          /* The widget has a grab and is being told about an
+           * enter-event outside its hierarchy. Hopefully we already
+           * got a leave-event, but if not, handle it now.
+           */
+          st_widget_set_hover (ST_WIDGET (actor), FALSE);
+        }
+    }
 
   if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event)
     return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event (actor, event);
@@ -1147,8 +1184,11 @@ st_widget_leave (ClutterActor         *actor,
 {
   StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 
-  if (priv->has_tooltip)
-    st_tooltip_hide (priv->tooltip);
+  if (priv->track_hover)
+    {
+      if (!actor_contains (actor, event->related))
+        st_widget_set_hover (ST_WIDGET (actor), FALSE);
+    }
 
   if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event)
     return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event (actor, event);
@@ -1267,8 +1307,9 @@ st_widget_class_init (StWidgetClass *klass)
   /**
    * StWidget:has-tooltip:
    *
-   * Determines whether the widget has a tooltip. If set to TRUE, causes the
-   * widget to monitor enter and leave events (i.e. sets the widget reactive).
+   * Determines whether the widget has a tooltip. If set to %TRUE, causes the
+   * widget to monitor hover state (i.e. sets #ClutterActor:reactive and
+   * #StWidget:track-hover).
    */
   pspec = g_param_spec_boolean ("has-tooltip",
                                 "Has Tooltip",
@@ -1292,6 +1333,40 @@ st_widget_class_init (StWidgetClass *klass)
                                ST_PARAM_READWRITE);
   g_object_class_install_property (gobject_class, PROP_TOOLTIP_TEXT, pspec);
 
+  /**
+   * StWidget:track-hover:
+   *
+   * Determines whether the widget tracks pointer hover state. If
+   * %TRUE (and the widget is visible and reactive), the
+   * #StWidget:hover property and "hover" style pseudo class will be
+   * adjusted automatically as the pointer moves in and out of the
+   * widget.
+   */
+  pspec = g_param_spec_boolean ("track-hover",
+                                "Track hover",
+                                "Determines whether the widget tracks hover state",
+                                FALSE,
+                                ST_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class,
+                                   PROP_TRACK_HOVER,
+                                   pspec);
+
+  /**
+   * StWidget:hover:
+   *
+   * Whether or not the pointer is currently hovering over the widget. This is
+   * only tracked automatically if #StWidget:track-hover is %TRUE, but you can
+   * adjust it manually in any case.
+   */
+  pspec = g_param_spec_boolean ("hover",
+                                "Hover",
+                                "Whether the pointer is hovering over the widget",
+                                FALSE,
+                                ST_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class,
+                                   PROP_HOVER,
+                                   pspec);
+
   /**
    * StWidget::style-changed:
    *
@@ -1794,10 +1869,10 @@ st_widget_set_direction (StWidget *self, StTextDirection dir)
  *
  * Enables tooltip support on the #StWidget.
  *
- * Note that setting has-tooltip to %TRUE will cause the widget to be set
- * reactive. If you no longer need tooltip support and do not need the widget
- * to be reactive, you need to set ClutterActor::reactive to FALSE.
- *
+ * Note that setting has-tooltip to %TRUE will cause
+ * #ClutterActor:reactive and #StWidget:track-hover to be set %TRUE as
+ * well, but you must clear these flags yourself (if appropriate) when
+ * setting it %FALSE.
  */
 void
 st_widget_set_has_tooltip (StWidget *widget,
@@ -1814,6 +1889,7 @@ st_widget_set_has_tooltip (StWidget *widget,
   if (has_tooltip)
     {
       clutter_actor_set_reactive ((ClutterActor*) widget, TRUE);
+      st_widget_set_track_hover (widget, TRUE);
 
       if (!priv->tooltip)
         {
@@ -1948,3 +2024,143 @@ st_widget_hide_tooltip (StWidget *widget)
   if (widget->priv->tooltip)
     st_tooltip_hide (widget->priv->tooltip);
 }
+
+/**
+ * st_widget_set_track_hover:
+ * @widget: A #StWidget
+ * @track_hover: %TRUE if the widget should track the pointer hover state
+ *
+ * Enables hover tracking on the #StWidget.
+ *
+ * If hover tracking is enabled, and the widget is visible and
+ * reactive, then @widget's #StWidget:hover property will be updated
+ * automatically to reflect whether the pointer is in @widget (or one
+ * of its children), and @widget's #StWidget:pseudo-class will have
+ * the "hover" class added and removed from it accordingly.
+ *
+ * Note that currently it is not possible to correctly track the hover
+ * state when another actor has a pointer grab. You can use
+ * st_widget_sync_hover() to update the property manually in this
+ * case.
+ */
+void
+st_widget_set_track_hover (StWidget *widget,
+                           gboolean  track_hover)
+{
+  StWidgetPrivate *priv;
+
+  g_return_if_fail (ST_IS_WIDGET (widget));
+
+  priv = widget->priv;
+
+  if (priv->track_hover != track_hover)
+    {
+      priv->track_hover = track_hover;
+      g_object_notify (G_OBJECT (widget), "track-hover");
+
+      if (priv->track_hover)
+        st_widget_sync_hover (widget);
+    }
+}
+
+/**
+ * st_widget_get_track_hover:
+ * @widget: A #StWidget
+ *
+ * Returns the current value of the track-hover property. See
+ * st_tooltip_set_track_hover() for more information.
+ *
+ * Returns: current value of track-hover on @widget
+ */
+gboolean
+st_widget_get_track_hover (StWidget *widget)
+{
+  g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
+
+  return widget->priv->track_hover;
+}
+
+/**
+ * st_widget_set_hover:
+ * @widget: A #StWidget
+ * @hover: whether the pointer is hovering over the widget
+ *
+ * Sets @widget's hover property and adds or removes "hover" from its
+ * pseudo class accordingly. If #StWidget:has-tooltip is %TRUE, this
+ * will also show or hide the tooltip, as appropriate.
+ *
+ * If you have set #StWidget:track-hover, you should not need to call
+ * this directly. You can call st_widget_sync_hover() if the hover
+ * state might be out of sync due to another actor's pointer grab.
+ */
+void
+st_widget_set_hover (StWidget *widget,
+                     gboolean  hover)
+{
+  StWidgetPrivate *priv;
+
+  g_return_if_fail (ST_IS_WIDGET (widget));
+
+  priv = widget->priv;
+
+  if (priv->hover != hover)
+    {
+      priv->hover = hover;
+      if (priv->hover)
+        {
+          st_widget_add_style_pseudo_class (widget, "hover");
+          if (priv->has_tooltip)
+            st_widget_show_tooltip (widget);
+        }
+      else
+        {
+          st_widget_remove_style_pseudo_class (widget, "hover");
+          if (priv->has_tooltip)
+            st_widget_hide_tooltip (widget);
+        }
+      g_object_notify (G_OBJECT (widget), "hover");
+    }
+}
+
+/**
+ * st_widget_sync_hover:
+ * @widget: A #StWidget
+ *
+ * Sets @widget's hover state according to the current pointer
+ * position. This can be used to ensure that it is correct after
+ * (or during) a pointer grab.
+ */
+void
+st_widget_sync_hover (StWidget *widget)
+{
+  ClutterDeviceManager *device_manager;
+  ClutterInputDevice *pointer;
+  ClutterActor *actor;
+
+  device_manager = clutter_device_manager_get_default ();
+  pointer = clutter_device_manager_get_core_device (device_manager,
+                                                    CLUTTER_POINTER_DEVICE);
+  actor = clutter_input_device_get_pointer_actor (pointer);
+
+  while (actor && actor != (ClutterActor *)widget)
+    actor = clutter_actor_get_parent (actor);
+
+  st_widget_set_hover (widget, actor == (ClutterActor *)widget);
+}
+
+/**
+ * st_widget_get_hover:
+ * @widget: A #StWidget
+ *
+ * If #StWidget:track-hover is set, this returns whether the pointer
+ * is currently over the widget.
+ *
+ * Returns: current value of hover on @widget
+ */
+gboolean
+st_widget_get_hover (StWidget *widget)
+{
+  g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
+
+  return widget->priv->hover;
+}
index 660c5e584f36b1c3ec0637fb76d624bef543e2ff..b6e2766a1a748274bdf2754707bebabc1774ecc8 100644 (file)
@@ -120,6 +120,14 @@ const gchar*          st_widget_get_tooltip_text          (StWidget        *widg
 void                  st_widget_show_tooltip              (StWidget        *widget);
 void                  st_widget_hide_tooltip              (StWidget        *widget);
 
+void                  st_widget_set_track_hover           (StWidget        *widget,
+                                                           gboolean         track_hover);
+gboolean              st_widget_get_track_hover           (StWidget        *widget);
+void                  st_widget_set_hover                 (StWidget        *widget,
+                                                           gboolean         hover);
+void                  st_widget_sync_hover                (StWidget        *widget);
+gboolean              st_widget_get_hover                 (StWidget        *widget);
+
 void                  st_widget_ensure_style              (StWidget        *widget);
 
 StTextDirection       st_widget_get_default_direction     (void);